﻿namespace Example.Xml
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.XPath;

    public sealed class ValidatedXmlDocument
    {
        public ValidatedXmlDocument(string xml, Uri schema)
            : this(xml, null, schema)
        {
        }

        public ValidatedXmlDocument(string xml, string ns, Uri schema)
        {
            var settings = new XmlReaderSettings();
            settings.Schemas.Add(ns, schema.IsAbsoluteUri ? schema.AbsoluteUri : schema.OriginalString);
            settings.Schemas.Compile();
            settings.ValidationType = ValidationType.Schema;
            settings.ValidationEventHandler += OnValidationEvent;

            using (var stream = new MemoryStream())
            {
                using (var writer = new StreamWriter(stream))
                {
                    writer.Write(xml);
                    writer.Flush();
                    stream.Position = 0;

                    using (var reader = XmlReader.Create(stream, settings))
                    {
                        while (reader.Read())
                        {
                            // optional element examination here;
                            // The ValidationEvent event is raised whenever an error is encountered
                        }
                    }
                }
            }

            LoadXml(xml);
        }

        public IXPathNavigable Document { get; set; }

        public XmlNamespaceManager ToXmlNamespaceManager(string prefix, Uri uri)
        {
            return ToXmlNamespaceManager(new KeyValuePair<string, Uri>(prefix, uri));
        }

        public XmlNamespaceManager ToXmlNamespaceManager(params KeyValuePair<string, Uri>[] namespaces)
        {
            return ToXmlNamespaceManager((XmlDocument)Document, namespaces);
        }

        private static void OnValidationEvent(object sender, ValidationEventArgs arguments)
        {
            throw new XmlException("The xml is invalid: " + arguments.Message);
        }

        private static XmlNamespaceManager ToXmlNamespaceManager(XmlDocument document, params KeyValuePair<string, Uri>[] namespaces)
        {
            var manager = new XmlNamespaceManager(document.NameTable);

            foreach (var item in namespaces)
            {
                manager.AddNamespace(item.Key, item.Value.AbsoluteUri);
            }

            return manager;
        }

        private void LoadXml(string xml)
        {
            var document = new XmlDocument();
            document.LoadXml(xml);

            Document = document;
        }
    }
}